package com.zbf.utils;


import com.alibaba.fastjson.JSON;
import com.lowagie.text.pdf.BaseFont;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import io.minio.MinioClient;
import io.minio.PutObjectOptions;
import io.minio.errors.*;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.w3c.dom.Document;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 功能：pdf处理工具类
 * https://blog.csdn.net/xt7821096/article/details/79583529
 */
public class PdfUtils {

    private PdfUtils() {
    }

    // 临时文件夹获取java system变量中的临时路径，在web项目中是容器的temp文件夹,如果直接运行是系统临时文件夹.
    private static final String FILE_PATH_TEMPLATE = System.getProperty("java.io.tmpdir") + "/tempdf/%s";

    private static final Logger LOGGER = LoggerFactory.getLogger(PdfUtils.class);

    /**
     * 按模板和参数生成html字符串,再转换为flying-saucer识别的Document
     *
     * @param templateName freemarker模板名称
     * @param variables    freemarker模板参数
     * @return Document
     */
    private static Document generateDoc(FreeMarkerConfigurer configurer, String templateName, Map<String, Object> variables)  {
        Template tp;
        try {
            tp = configurer.getConfiguration().getTemplate(templateName);
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
            return null;
        }

        StringWriter stringWriter = new StringWriter();
        try(BufferedWriter writer = new BufferedWriter(stringWriter)) {
            try {
                tp.process(variables, writer);
                writer.flush();
            } catch (TemplateException e) {
                LOGGER.error("模板不存在或者路径错误", e);
            } catch (IOException e) {
                LOGGER.error("IO异常", e);
            }
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            return builder.parse(new ByteArrayInputStream(stringWriter.toString().getBytes()));
        }catch (Exception e){
            LOGGER.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 核心: 根据freemarker模板生成pdf文档
     *
     * @param configurer   freemarker配置
     * @param templateName freemarker模板名称
     * @param out          输出流
     * @param listVars     freemarker模板参数
     * @throws Exception 模板无法找到、模板语法错误、IO异常
     */
    private static void generateAll(FreeMarkerConfigurer configurer, String templateName, OutputStream out, List<Map<String, Object>> listVars) throws Exception {
        if (CollectionUtils.isEmpty(listVars)) {
            LOGGER.warn("警告:freemarker模板参数为空!");
            return;
        }

        ITextRenderer renderer = new ITextRenderer();
        Document doc = generateDoc(configurer, templateName, listVars.get(0));
        renderer.setDocument(doc, null);
        //设置字符集(宋体),此处必须与模板中的<body style="font-family: SimSun">一致,区分大小写,不能写成汉字"宋体"
        ITextFontResolver fontResolver = renderer.getFontResolver();
        fontResolver.addFont("simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        //展现和输出pdf
        renderer.layout();
        renderer.createPDF(out, false);

        //根据参数集个数循环调用模板,追加到同一个pdf文档中
        //(注意:此处从1开始,因为第0是创建pdf,从1往后则向pdf中追加内容)
        for (int i = 1; i < listVars.size(); i++) {
            Document docAppend = generateDoc(configurer, templateName, listVars.get(i));
            renderer.setDocument(docAppend, null);
            renderer.layout();
            renderer.writeNextDocument(); //写下一个pdf页面
        }
        renderer.finishPDF(); //完成pdf写入



    }

    /**
     * pdf下载
     *
     * @param configurer   freemarker配置
     * @param templateName freemarker模板名称(带后缀.ftl)
     * @param listVars     模板参数集
     * @param response     HttpServletResponse
     * @param fileName     下载文件名称(带文件扩展名后缀)
     */
    public static void download(FreeMarkerConfigurer configurer, String templateName, List<Map<String, Object>> listVars, HttpServletResponse response, String fileName) {
        // 设置编码、文件ContentType类型、文件头、下载文件名
        response.setCharacterEncoding("utf-8");
        response.setContentType("multipart/form-data");
        try {
            response.setHeader("Content-Disposition", "attachment;fileName=" +
                    new String(fileName.getBytes("gb2312"), "ISO8859-1"));
        } catch (UnsupportedEncodingException e) {
            LOGGER.error(e.getMessage(), e);
        }
        try (ServletOutputStream out = response.getOutputStream()) {
            generateAll(configurer, templateName, out, listVars);
            out.flush();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

    /**
     * pdf预览
     *
     * @param configurer   freemarker配置
     * @param templateName freemarker模板名称(带后缀.ftl)
     * @param listVars     模板参数集
     * @param response     HttpServletResponse
     */
    public static void preview(FreeMarkerConfigurer configurer, String templateName, List<Map<String, Object>> listVars, HttpServletResponse response) {
        try (ServletOutputStream out = response.getOutputStream()) {
            generateAll(configurer, templateName, out, listVars);
            out.flush();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }


    /**
     * 作者: LCG
     * 日期: 2021/6/9  0:33
     * 描述: 获取文档的字符串
     * @Param []
     * @Return java.lang.String
     */
    public static String getDocString(FreeMarkerConfigurer configurer, String templateName, Map<String, Object> param){
        try{
            Template tp = configurer.getConfiguration().getTemplate(templateName);
            String resStr = FreeMarkerTemplateUtils.processTemplateIntoString(tp, param);
            return resStr;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }



    /**
     * 作者: LCG
     * 日期: 2021/6/9  15:32
     * 描述: 远程调用第三方的接口
     * @Param [param, httpurl]
     * @Return java.lang.String
     */
    public static String remoteServer(Map<String,Object> param,String httpurl) throws IOException {
        //1构建OKHttpClient客户端
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                //发请求给对方传递数据的超时时间
                .writeTimeout(100, TimeUnit.SECONDS)
                //接收请求返回的数据的超时时间(适合于对方服务器处理业务时间比较长的场景)
                .readTimeout(5, TimeUnit.SECONDS)
                //链接的超时时间
                .connectTimeout(100, TimeUnit.SECONDS)
                //等待创建请求的超时时间
                .callTimeout(100, TimeUnit.SECONDS)
                .build();
        //2构建form 表单 参数很多大家可以通过For循环去添加
        RequestBody requestBody = FormBody.create(MediaType.parse("application/json"), JSON.toJSONString(param));

        //3、构建请求
        Request request = new Request.Builder()
                .addHeader("token", "1234569877")
                .post(requestBody)
                .url(httpurl)
                .build();

        //4、发送请求 获取响应内容  记得自己 try catch 一下 我这里不写了 大家 仿照上文的  get请求去处理
        ResponseBody body = okHttpClient.newCall(request).execute().body();

        return new String(body.bytes(),"utf-8");
    }



    /**
     * 作者: LCG
     * 日期: 2021/6/9  16:31
     * 描述: 生成PDF上传到minio服务器
     * @Param [
     * fielName pdf的文件名字，带后缀
     * minioClient,
     * bucket,  minio的目录
     * configurer,
     * templateName,模板
     * variables  模板参数
     * ]
     * @Return 返回minio的文件路径和文件的字符信息
     */
    public static  Map<String,String> uploadToMonio(String fielName,MinioClient minioClient,String bucket,FreeMarkerConfigurer configurer, String templateName, Map<String, Object> variables) throws Exception{

        //生成临时文件位置，存储到 java.io.tmpdir 下
        File outFile = new File(String.format(FILE_PATH_TEMPLATE, fielName));
        //如果不存在临时文件夹，则创建文件夹
        if (!outFile.getParentFile().exists()) {
            outFile.getParentFile().mkdirs();
        }
        System.out.println("临时文件所在位置：——" + outFile.getPath());
        FileOutputStream outputStream = new FileOutputStream(outFile);
        List<Map<String, Object>> list=new ArrayList<>();
        list.add(variables);
        generateAll(configurer,templateName,outputStream,list);
        //上传
        FileInputStream fileInputStream=new FileInputStream(outFile);
        PutObjectOptions putObjectOptions=new PutObjectOptions(fileInputStream.available(),-1);
        //设置文件的内容类型
        putObjectOptions.setContentType("application/pdf");
        minioClient.putObject(bucket,fielName,fileInputStream,putObjectOptions);
        String images1 = minioClient.presignedGetObject(bucket,fielName, 50000);
        Map<String,String> map=new HashMap<>();
        //返回minio的路径
        map.put("filePath",images1);

        //删除临时文件
        if (outFile.isFile() && outFile.exists()) {
            outFile.delete();
        }

        return map;

    }



    private static Map<String,Object> generateDocInputStream(FreeMarkerConfigurer configurer, String templateName, Map<String, Object> variables)  {
        Template template;
        try {
            template = configurer.getConfiguration().getTemplate(templateName);
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
            return null;
        }

        StringWriter stringWriter = new StringWriter();
        try(BufferedWriter writer = new BufferedWriter(stringWriter)) {
            try{
                template.process(variables, writer);
                writer.flush();
            } catch (TemplateException e) {
                LOGGER.error("模板不存在或者路径错误", e);
            } catch (IOException e) {
                LOGGER.error("IO异常", e);
            }

            Map<String,Object> map=new HashMap<>();
            map.put("docStream",new ByteArrayInputStream(stringWriter.toString().getBytes()));
            map.put("docString",stringWriter.toString());
            return map;
        }catch (Exception e){
            LOGGER.error(e.getMessage(), e);
            return null;
        }
    }



    public static void xizai(MinioClient minioClient,String filePath,String fileName,String bacuk,HttpServletResponse response) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        InputStream images = minioClient.getObject(bacuk,fileName);
        byte[] bytes=new byte[1024];
        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
        int rc = 0;
        while((rc=images.read(bytes))>0){
            byteArrayOutputStream.write(bytes,0,rc);
        }
        response.setCharacterEncoding("utf-8");
        response.addHeader("Content-Disposition", "attachment;filename="+new String(fileName.getBytes(),"ISO-8859-1"));
        response.getOutputStream().write(byteArrayOutputStream.toByteArray());
    }

}
